<?php
namespace Sfsn\Core\Http;

/**
 * 路由类，接收处理和输出处理url
 * 在common函数类库生成url和App初始文件的url解析中都用到
 *
 * @author shooke
 */
class Route
{

    /**
     * 配置数组
     *
     * @var array
     */
    public static $config = [
        /**
         * URL_TYPE 是否开启重写，偶数带有执行文件 奇数因此执行文件
         * 1 index.php?g=group&m=module&a=action
         * 2 ?g=group&m=module&a=action
         * 3 index.php?r=/group/module/action
         * 4 ?r=/group/module/action
         * 5 index.php/group/module/action
         * 6 /group/module/action
         */
        'URL_TYPE' => 1,
        'URL_GMA_VAR' => 'g,m,a', // url变量，URL_REWRITE_ON为1 2时生成原生地址时使用，默认是'g,m,a' 生成地址为 'g=group&m=controller&a=method'
        'URL_R_VAR' => 'r', // url变量，URL_REWRITE_ON为4 5时生成原生地址时使用，默认是'r' 生成地址为 r=/group/module/action
        'URL_GROUP_DEPR' => '/', // 组分隔符，一般不需要修改
        'URL_MODULE_DEPR' => '/', // 控制器分隔符，一般不需要修改
        'URL_ACTION_DEPR' => '-', // 方法分隔符，一般不需要修改
        'URL_PARAM_DEPR' => '-', // 参数分隔符，一般不需要修改
        'URL_SUFFIX_ON' => true, // 伪静态后缀设置开关，开启后会在生成的url中追加URL_SUFFIX设置的后缀，关闭后会过滤后缀
        'URL_SUFFIX' => '.html'
    ] // 伪静态后缀设置，例如 .html ，一般不需要修改
;
    // 自定义路由，可用来实现restful
    
    // 分组配置
    // 'GROUP_DEFAULT'=>'home',
    // 'GROUP_LIST'=>[
    // 'home'=>[
    // 'DOMAIN'=>'baidu.com'
    // ],
    // ],
    
    /**
     * 用来存放Sfsn\Core\Http\Reauest对象
     *
     * @var object
     */
    public static $request = null;

    /**
     * 分组
     *
     * @var string
     */
    public static $group = null;

    /**
     * 模块
     *
     * @var string
     */
    public static $module = null;

    /**
     * 操作方法
     *
     * @var string
     */
    public static $action = null;

    /**
     * 数据
     *
     * @var array
     */
    public static $param = null;

    /**
     * 参数状态，获取参数时使用
     *
     * @var array
     */
    public static $status = array(
        'groupIsDefault' => false,
        'moduleIsDefault' => false,
        'actionIsDefault' => false
    );

    /**
     * 对象实例化时用于初始化配置
     *
     * @param array $config            
     */
    public function setConfig($config = [])
    {
        self::$config = array_merge(self::$config, $config);
    }

    /**
     * 设置Request对象
     *
     * @return Request
     */
    public static function setRequest($request)
    {
        self::$request = $request;
        return self::$request;
    }

    /**
     * 返回分组 模块 方法以及数据
     *
     * @return multitype:\Sfsn\Core\Http\string \Sfsn\Core\Http\array
     */
    public static function getData()
    {
        $output = [
            'group' => self::$group,
            'module' => self::$module,
            'action' => self::$action,
            'param' => self::$param
        ];
        return $output;
    }
    // +----------------------------------------------------------------------
    // | 下面开始是接收处理
    // +----------------------------------------------------------------------
    
    /**
     * 处理restful请求
     */
    public static function restful()
    {
        $param = [];
        $config = self::$config;
        $request = self::$request;
        $type = $request->getMethod();
        $pathInfo = $request->getPathInfo();
        foreach ($config['RESTFUL'] as $key => $val) {
            $param = explode('/', $pathInfo);
            if ($param) {
                if (strtoupper($val['method']) == $type && $param[1] == $val['route']) {
                    $gma = explode('/', $key);
                    if (count($gma) == 2) {
                        self::$module = $gma[0];
                        self::$action = $gma[1];
                    }
                    if (count($gma) == 3) {
                        self::$group = $gma[0];
                        self::$module = $gma[1];
                        self::$action = $gma[2];
                    }
                    self::$param = array_merge($param, $request->get());
                }
            }
        }
    }

    /**
     * 原生url参数获取
     * 0 index.php?g=group&m=module&a=action
     * 1 ?g=group&m=module&a=action
     */
    public static function gmaUrl()
    {
        $config = self::$config;
        $request = self::$request;
        $get = $request->get();
        
        // 设置默认值
        $group = $module = $action = '';
        // $g = $m = $a = '';
        list ($g, $m, $a) = explode(',', $config['URL_GMA_VAR']);
        // 获取原生地址变量名
        if ($config['GROUP_DEFAULT']) {
            // 根据域名确定分组
            foreach ($config['GROUP_LIST'] as $key => $val) {
                if ($val['DOMAIN'] == $_SERVER['HTTP_HOST']) {
                    $group = $key; // 正式获得分组
                    break;
                }
            }
            // 根据参数获取分组
            if (empty($group)) {
                if (empty($get[$g])) {
                    self::$status['groupIsDefault'] = true; // 分组参数默认值标记
                    $group = $config['GROUP_DEFAULT']; // 默认值处理
                } else {
                    $group = $get[$g]; // 使用获取到的值
                }
            }
        }
        
        // 参数默认值标记
        ! empty($get[$m]) && self::$status['moduleIsDefault'] = true;
        ! empty($get[$a]) && self::$status['actionIsDefault'] = true;
        // 默认值处理
        $module = empty($get[$m]) ? $config['MODULE_DEFAULT'] : $get[$m];
        $action = empty($get[$a]) ? $config['ACTION_DEFAULT'] : $get[$a];
        
        // 将结果传递给静态变量
        self::$group = self::_bigCamel($group);//转换大驼峰命名
        self::$module = self::_bigCamel($module);//转换大驼峰命名
        self::$action = self::_littleCamel($action);//转换小驼峰命名
        self::$param = $get;
    }
    // 网址解析
    public static function parseUrl()
    {
        $config = self::$config;
        $request = self::$request;
        $url = '';
        
        /**
         * 3 index.php?r=/group/module/action
         * 4 ?r=/group/module/action
         */
        if ($config['URL_TYPE'] > 2) {
            $r = $request->get($config['URL_R_VAR']);
            $url = empty($r) ? '/' : $r;
        }
        
        /**
         * 5 index.php/group/module/action
         * 6 /group/module/action
         */
        if ($config['URL_TYPE'] > 4) {
            $url = $request->getPathInfo();
        }
        // 第一个字符是'/'，则去掉
        if ($url[0] == '/') {
            $url = substr($url, 1);
        }
        
        // 去除后缀
        $pos = (int) strrpos($url, $config['URL_SUFFIX']);
        if ($url && $pos > 0) {
            $url = substr($url, 0, $pos);
        }
        
        $url = self::_getGroup($url); // 获取分组名称        
        $url = self::_getModule($url); // 获取模块名称
        $url = self::_getAction($url); // 获取操作方法名称
        
        self::_getParam($url); // 获取其他参数
    }

    /**
     * 获取group参数，分组
     *
     * @param unknown $url            
     * @return string
     */
    private static function _getGroup($url)
    {
        $config = self::$config;
        $group = '';
        
        // 未进行域名绑定
        if ($url && ($pos = strpos($url, $config['URL_GROUP_DEPR'], 1)) > 0) {
            $group = substr($url, 0, $pos); // 分组
            $group = self::_bigCamel($group);//转换大驼峰命名
            if (array_key_exists($group, $config['GROUP_LIST'])) { // 分组目录中有对应分组
                self::$group = $group; // 正式获得分组
                $url = substr($url, $pos + 1); // 除去分组名称，剩下的url字符串
            }
        } else { // url找不到分组时，直接用url作为分组
            $url = self::_bigCamel($url);//转换大驼峰命名
            if (array_key_exists($url, $config['GROUP_LIST'])) { // 分组目录中有对应分组
                self::$group = $url; // 正式获得分组
                $url = ''; // 清除剩余url
            }
        }
        
        // 如果进过url解析还是未获得分组，则采用默认值
        if (empty(self::$group)) {
            self::$status['groupIsDefault'] = true; // 分组参数默认值标记
            self::$group = $config['GROUP_DEFAULT']; // 设置默认值
        }        
        
        return $url;
    }

    /**
     * 获取module参数，模块类
     *
     * @param unknown $url            
     * @return string
     */
    private static function _getModule($url)
    {
        $config = self::$config;
        if ($url && ($pos = @strpos($url, $config['URL_MODULE_DEPR'], 1)) > 0) {
            $module = substr($url, 0, $pos); // 模块
            self::$module = self::_bigCamel($module);//转换大驼峰命名
            $url = substr($url, $pos + 1); // 除去模块名称，剩下的url字符串
        } else {
            $url = self::_bigCamel($url);//转换大驼峰命名
            self::$module = $url; // 找不到，把剩余url作为参数
            $url = ''; // 清除剩余url
        }
        
        // 如果进过url解析还是未获得模块，则采用默认值
        if (empty(self::$module)) {
            self::$status['moduleIsDefault'] = true; // 模块参数默认值标记
            self::$module = $config['MODULE_DEFAULT']; // 设置默认值
        }        
       
        // 返回剩余url
        return $url;
    }

    /**
     * 获取action参数，操作方法
     *
     * @param unknown $url            
     * @return string
     */
    private static function _getAction($url)
    {
        $config = self::$config;
        if ($url && ($pos = @strpos($url, $config['URL_ACTION_DEPR'], 1)) > 0) {
            $action = substr($url, 0, $pos); // 模块
            self::$action = self::_littleCamel($action);//转换小驼峰命名
            $url = substr($url, $pos + 1);
        } else {
            $url = self::_littleCamel($url);//转换小驼峰命名
            self::$action = $url; // 找不到，把剩余url作为参数
            $url = ''; // 清除剩余url
        }
        
        // 如果进过url解析还是未获得方法，则采用默认值
        if (empty(self::$action)) {
            self::$status['actionIsDefault'] = true; // 方法参数默认值标记
            self::$action = $config['ACTION_DEFAULT']; // 设置默认值
        }
        // 返回剩余url
        return $url;
    }

    private static function _getParam($url)
    {
        $config = self::$config;
        $request = self::$request;
        $param = null;
        $paramArr = explode($config['URL_PARAM_DEPR'], $url);
        $param_count = count($paramArr);
        for ($i = 0; $i < $param_count; $i = $i + 2) {
            $param[$i] = $paramArr[$i];
            if (isset($paramArr[$i + 1])) {
                if (! is_numeric($paramArr[$i])) {
                    $param[$paramArr[$i]] = $paramArr[$i + 1];
                }
                // $param[$i + 1] = $paramArr[$i + 1];
            }
        }
        // 添加$_GET请求数据
        $get = $request->get();
        if ($get) {
            foreach ($get as $key => $val) {
                $param[$key] = $val;
            }
        }
        //
        self::$param = $param;
    }
    // +----------------------------------------------------------------------
    // | 下面开始是输出处理
    // +----------------------------------------------------------------------
    
    /**
     * 生成url
     *
     * @access public
     * @param array $url
     *            地址参数url( '[模块/操作]','额外参数1=值1&额外参数2=值2...')
     *            支持 array('name'=>$value) 或者 name=$value
     * @return array
     */
    public static function url($module, $param = '', $author = '', $domain = null)
    {
        // 第一个字符是'/'，则去掉
        if ($module[0] == '/') {
            $module = substr($module, 1);
        }
        
        /**
         * URL_TYPE 是否开启重写，偶数带有执行文件 奇数因此执行文件
         * 1 index.php?g=group&m=module&a=action
         * 2 ?g=group&m=module&a=action
         * 3 index.php?r=/group/module/action
         * 4 ?r=/group/module/action
         * 5 index.php/group/module/action
         * 6 /group/module/action
         */
        $url_model = self::$config['URL_TYPE'];
        switch ($url_model) {
            case 1:
            case 2:
                return self::_gmaUrl($module, $param, $author, $domain);
            case 3:
            case 4:
            case 5:
            case 6:
                return self::_pathInfo($module, $param, $author, $domain);
        }
    }

    /**
     * 2 index.php/group/module/action
     * 3 /group/module/action
     *
     * @param unknown $gma
     *            模块 Group/Module/Action 大驼峰模式传入
     * @param string $request
     *            字符串或数组
     * @param string $author
     *            锚点连接
     * @return string
     */
    private static function _pathInfo($gma, $param = '', $author = '', $domain = null)
    {
        //转为下划线间隔
        $gma = self::_under($gma);
        $gma = str_replace('/_', '/', $gma);//group/_module/_action变为group/module/action
        $config = self::$config;
        $group = self::$group;
        $module = self::$module;
        $action = self::$action;
        $request = self::$request;
        $groupDefault = $config['GROUP_DEFAULT'];
        $groupDomain = $config['GROUP_LIST'][$group]['DOMAIN']; // 取得当前分组对应域名
        
        $groupDepr = $config['URL_GROUP_DEPR'];
        $moduleDepr = $config['URL_MODULE_DEPR'];
        $actionDepr = $config['URL_ACTION_DEPR'];
        $paramDepr = $config['URL_PARAM_DEPR'];
        
        $marray = explode('/', $gma);
        
        // 如果只有模块和操作方法
        if (count($marray) == 2) {
            $module = $marray[0] . $moduleDepr . $marray[1] . $actionDepr; // 生成module/action/格式
            if ($groupDefault) { // 分组开启状态
                if ($group != $groupDefault && empty($groupDomain)) { // 当前分组不是默认分组 并且没有设置域名
                    $module = $group . $groupDepr . $module; // 添加当前分组到url中 生成group/module/action/
                }
            }
        }
        // 有分组 模块 操作方法
        if (count($marray) == 3) {
            $module = $marray[1] . $moduleDepr . $marray[2] . $actionDepr; // module/action/格式 无需加入分组名，最后生成url时将域名加入
            if ($marray[0] != $groupDefault && empty($groupDomain)) { // 指定分组不是默认分组 并且没有设置域名
                $module = $marray[0] . $groupDepr . $module;
            }
        }
        if (! empty($param)) {
            if (is_array($param)) {
                $param = http_build_query($param);
            }
            $param = str_replace(array(
                '=',
                '&'
            ), array(
                $paramDepr,
                $paramDepr
            ), $param);
            $module = $module . $param;
        } else { // 没有$param参数
            $module = substr($module, 0, - 1); // 去除最后的action结尾分隔，符生成module/action格式
        } // end if $param
        
        $scriptName = $request->getScriptName();
        $http = $request->getScheme();
        $app = $config['URL_TYPE'] == 3 || $config['URL_TYPE'] == 5 ? $scriptName : str_replace('/'.basename($scriptName), '', $scriptName);
        // 根据url类型组合
        if ($config['URL_TYPE'] == 3 || $config['URL_TYPE'] == 4) {
            $module = '?' . $config['URL_R_VAR'] . '=' . $module;
        }else{
            $module = '/' . $module;
        }
        // 选择域名
        $domain = is_null($domain) ? $groupDomain : $domain;
        
        if ($domain) { // 有对应域名时生成地址加入域名
            $return = $http . '://' . $domain . $app . $module . $config['URL_SUFFIX'];
        } else {
            $return = $app . $module . $config['URL_SUFFIX'];
        }
        return $author ? $return . '#' . $author : $return;
    }

    /**
     * 0 index.php?g=group&m=module&a=action
     * 1 ?g=group&m=module&a=action
     *
     * @param string $gma
     *            模块 Group/Module/Action 大驼峰模式传入
     * @param array|string $param
     *            参数可以是数组也可以是url格式
     * @param string $author
     *            锚点
     * @param string $domain
     *            域名
     * @return string
     */
    private static function _gmaUrl($gma, $param = '', $author = '', $domain = null)
    {
        //转为下划线间隔
        $gma = self::_under($gma);
        $gma = str_replace('/_', '/', $gma);//group/_module/_action变为group/module/action
        $config = self::$config;
        $group = self::$group;
        $module = self::$module;
        $action = self::$action;
        $request = self::$request;
        $groupDefault = $config['GROUP_DEFAULT'];
        $groupDomain = $config[$group]['DOMAIN']; // 取得当前分组对应域名
        
        list ($g, $m, $a) = explode(',', $config['URL_GMA_VAR']);
        
        // 分解分组 模块 操作方法
        $marray = explode('/', $gma);
        
        // 如果只有模块和操作方法
        if (count($marray) == 2) {
            $module = $m . '=' . $marray[0] . '&' . $a . '=' . $marray[1]; // 生成m=module&a=action格式
            if ($groupDefault) { // 分组开启状态
                if ($group != $groupDefault && empty($groupDomain)) { // 当前分组不是默认分组 并且没有设置域名
                    $module = $g . '=' . $group . '&' . $module; // 添加当前分组到url中 生成g=group&m=module&a=action
                }
            }
        }
        // 有分组 模块 操作方法
        if (count($marray) == 3) {
            $module = $m . '=' . $marray[1] . '&' . $a . '=' . $marray[2]; // m=module&a=action格式 无需加入分组名，最后生成url时将域名加入
            if ($marray[0] != $groupDefault && empty($groupDomain)) { // 指定分组不是默认分组 并且没有设置域名
                $module = $g . '=' . $marray[0] . '&' . $module;
            }
        }
        if (! empty($param)) {
            is_array($param) && $param = http_build_query($param);
            $module = $module . '&' . $param;
        } // end if request
        $scriptName = $request->getScriptName();
        $http = $request->getScheme();
        $app = $config['URL_TYPE'] == 1 ? $scriptName : str_replace(basename($scriptName), '', $scriptName);
        // 选择域名
        $domain = is_null($domain) ? $groupDomain : $domain;
        
        if ($domain) { // 有对应域名时生成地址加入域名
            $return = $http . '://' . $domain . $app . '?' . $module;
        } else {
            $return = $app . '?' . $module;
        }
        return $author ? $return . '#' . $author : $return;
    }
    
    /**
     * 字符串转换为_间隔的匈牙利命名法如my_name
     * @param unknown $string
     * @return string
     */
    private static function _under($string){
        $temp_array = [];
        for($i=0;$i<strlen($string);$i++){
            $ascii_code = ord($string[$i]);
            if($ascii_code >= 65 && $ascii_code <= 90){
                if($i == 0){
                    $temp_array[] = chr($ascii_code + 32);
                }else{
                    $temp_array[] = _.chr($ascii_code + 32);
                }
            }else{
                $temp_array[] = $string[$i];
            }
        }
        return implode('',$temp_array);
    }
    /**
     * 字符串转换为大写字母间隔的大驼峰命名发如MyName
     * @param unknown $string
     * @return Ambigous <string, string>
     */
    private static function _bigCamel($string)
    {   
        $string = strtolower($string);//全部转为小写
        $output = '';
        $array = explode('_', $string);
        foreach ($array as $val){
            $output .= ucfirst($val);
        }
        return $output;
    }
    /**
     * 字符串转换为大写字母间隔的小驼峰命名发如myName
     * @param unknown $string
     * @return string
     */
    private static function _littleCamel($string)
    {
        $output = self::_bigCamel($string);  
        //首字母转为小写
        return lcfirst($output);
    }
}